#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stddef.h>
#include <limits.h>
#include <errno.h>
#include <math.h>
#include <float.h>
#include <ctype.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <inttypes.h>

#include "include/pf_json.h"
#include "include/pf_internal.h"

struct print_buf
{
    char* buf;
    int bpos;
    int size;
};

struct lh_entry
{
    void* k;                    /* The key. */
    const void* v;              /* The value. */
    struct lh_entry* next;      /* The next entry. */
    struct lh_entry* prev;      /* The previous entry. */
};

typedef void (lh_entry_free_f) (struct lh_entry* e);       /* callback function prototypes */

typedef unsigned long (lh_hash_f) (const void* k);         /* callback function prototypes */

typedef int (lh_equal_f) (const void* k1, const void* k2); /* callback function prototypes */

struct lh_table
{
    int size;                   /* Size of our hash. */
    int count;                  /* Numbers of entries. */
    int collisions;             /* Number of collisions. */
    int resizes;                /* Number of resizes. */
    int lookups;                /* Number of lookups. */
    int inserts;                /* Number of inserts. */
    int deletes;                /* Number of deletes. */
    const char* name;           /* Name of the hash table. */
    struct lh_entry* head;      /* The first entry. */
    struct lh_entry* tail;      /* The last entry. */
    struct lh_entry* table;
    lh_entry_free_f* free_fn;  /* A pointer onto the function responsible for freeing an entry. */
    lh_hash_f* hash_fn;
    lh_equal_f* equal_fn;
};

struct pf_json_object_iter
{
    char* key;
    struct pf_json_object* val;
    struct lh_entry* entry;
};

enum pf_json_tokener_state 
{
    PF_JSON_TOK_STATE_EATWS,
    PF_JSON_TOK_STATE_START,
    PF_JSON_TOK_STATE_FINISH,
    PF_JSON_TOK_STATE_NULL,
    PF_JSON_TOK_STATE_COMMENT_START,
    PF_JSON_TOK_STATE_COMMENT,
    PF_JSON_TOK_STATE_COMMENT_EOL,
    PF_JSON_TOK_STATE_COMMENT_END,
    PF_JSON_TOK_STATE_STRING,
    PF_JSON_TOK_STATE_STRING_ESCAPE,
    PF_JSON_TOK_STATE_ESCAPE_UNICODE,
    PF_JSON_TOK_STATE_BOOLEAN,
    PF_JSON_TOK_STATE_NUMBER,
    PF_JSON_TOK_STATE_ARRAY,
    PF_JSON_TOK_STATE_ARRAY_ADD,
    PF_JSON_TOK_STATE_ARRAY_SEP,
    PF_JSON_TOK_STATE_OBJECT_FIELD_START,
    PF_JSON_TOK_STATE_OBJECT_FIELD,
    PF_JSON_TOK_STATE_OBJECT_FIELD_END,
    PF_JSON_TOK_STATE_OBJECT_VALUE,
    PF_JSON_TOK_STATE_OBJECT_VALUE_ADD,
    PF_JSON_TOK_STATE_OBJECT_SEP
};

struct pf_json_tokener_srec
{
    enum pf_json_tokener_state state, saved_state;
    struct pf_json_object* obj;
    struct pf_json_object* current;
    char* obj_field_name;
};

#define PF_JSON_TOKENER_MAX_DEPTH 32

struct pf_json_tokener
{
    char* str;
    struct print_buf* pb;
    int depth, is_double, st_pos, char_offset;
    enum pf_json_tokener_error err;
    unsigned int ucs_char;
    char quote_char;
    struct pf_json_tokener_srec stack[PF_JSON_TOKENER_MAX_DEPTH];
};

typedef void (array_list_free_f) (void* data);

struct array_list
{
    void** array;
    int length;
    int size;
    array_list_free_f* free_fn;
};

typedef void (pf_json_obj_del_f)(struct pf_json_object* o);

typedef int (pf_json_obj_to_str_f)(struct pf_json_object* o, struct print_buf* pb);

struct pf_json_object
{
    enum pf_json_type o_type;
    pf_json_obj_del_f* del_fn;
    pf_json_obj_to_str_f* to_str_fn;
    int _ref_count;
    struct print_buf* pb;
    union data {
        boolean c_boolean;
        double c_double;
        int64_t c_int64;
        struct lh_table* c_object;
        struct array_list* c_array;
        struct {
            char* str;
            int len;
        } c_string;
    } o;
};

#define PRINT_BUF_MEM_APPEND_FAST(p, bufptr, bufsize)          \
    do {                                                         \
    if((p->size - p->bpos) > bufsize) {                       \
    memcpy(p->buf + p->bpos, (bufptr), bufsize);             \
    p->bpos += bufsize;                                      \
    p->buf[p->bpos]= '\0';                                   \
    } else {  print_buf_mem_append(p, (bufptr), bufsize); }      \
    } while (0)

#define PF_JSON_MIN(a,b) ((a) < (b) ? (a) : (b))

#define PF_JSON_MAX(a,b) ((a) > (b) ? (a) : (b))

#define HEXDIGIT(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)

#define LH_PRIME 0x9e370001UL           /* golden prime used in hash functions */

#define LH_EMPTY (void*)-1              /* sentinel pointer value for empty slots */ 

#define LH_FREED (void*)-2              /* sentinel pointer value for freed slots*/

#define PF_JSON_ARRAY_LIST_DEF_SIZE 32

#define PF_JSON_OBJ_DEF_HASH_ENTRIES 16

#define FALSE ((boolean)0)

#define TRUE ((boolean)1)

#define PF_JSON_FILE_BUF_SIZE 4096

static struct lh_table* pf_json_obj_get_obj(pf_json_object_t* obj);

#define pf_json_obj_for_each_obj(obj,iter) \
    for(iter.entry = pf_json_obj_get_obj(obj)->head; \
        (iter.entry ? (iter.key = (char*)iter.entry->k, \
                       iter.val = (struct pf_json_object*)iter.entry->v, iter.entry) : 0); \
        iter.entry = iter.entry->next)

#define lh_for_each(table, entry) \
    for(entry = table->head; entry; entry = entry->next)

#define lh_for_each_safe(table, entry, tmp) \
    for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp)

#ifdef DEBUG
#define PF_JSON_SET_SYSLOG(x) pf_json_set_syslog(x)

#define PF_JSON_ABORT(x, ...) pf_json_abort(x, ##__VA_ARGS__)

#define PF_JSON_DEBUG(x, ...) pf_json_debug(x, ##__VA_ARGS__)

#define PF_JSON_ERROR(x, ...) pf_json_error(x, ##__VA_ARGS__)

#define PF_JSON_INFO(x, ...) pf_json_info(x, ##__VA_ARGS__)
#else
#define PF_JSON_SET_SYSLOG(x) if(0) pf_json_set_syslog(x)

#define PF_JSON_ABORT(x, ...) if(0) pf_json_abort(x, ##__VA_ARGS__)

#define PF_JSON_DEBUG(x, ...) if(0) pf_json_debug(x, ##__VA_ARGS__)

#define PF_JSON_ERROR(x, ...) if(0) pf_json_error(x, ##__VA_ARGS__)

#define PF_JSON_INFO(x, ...) if(0) pf_json_info(x, ##__VA_ARGS__)
#endif

#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800)

#define IS_LOW_SURROGATE(uc)  (((uc) & 0xFC00) == 0xDC00)

#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000)

#define PF_JSON_TOKENER_STATE  tok->stack[tok->depth].state

#define PF_JSON_TOKENER_SAVED_STATE  tok->stack[tok->depth].saved_state

#define PF_JSON_TOKENER_CURRENT tok->stack[tok->depth].current

#define PF_JSON_TOKENER_OBJ_FIELD_NAME tok->stack[tok->depth].obj_field_name

/* Optimization:
 pf_json_parse_ex() consumed a lot of CPU in its main loop,
 iterating character-by character.  A large performance boost is
 achieved by using tighter loops to locally handle units such as
 comments and strings.  Loops that handle an entire token within 
 their scope also gather entire strings and pass them to 
 print_buf_mem_append() in a single call, rather than calling
 print_buf_mem_append() one char at a time.

 PF_JSON_POP_CHAR() and PF_JSON_ADVANCE_CHAR() macros are used for code that is
 common to both the main loop and the tighter loops.
*/

/* PF_JSON_POP_CHAR(dest, tok) macro:
   Not really a pop()...peeks at the current char and stores it in dest.
   Returns 1 on success, sets tok->err and returns 0 if no more chars.
   Implicit inputs:  str, len vars
*/
#define PF_JSON_POP_CHAR(dest, tok)                                                  \
    (((tok)->char_offset == len) ?                                          \
    (((tok)->depth == 0 && PF_JSON_TOKENER_STATE == PF_JSON_TOK_STATE_EATWS && PF_JSON_TOKENER_SAVED_STATE == PF_JSON_TOK_STATE_FINISH) ? \
    (((tok)->err = PF_JSON_TOK_SUCCESS), 0)                              \
    :                                                                   \
    (((tok)->err = PF_JSON_TOK_CONTINUE), 0)                             \
    ) :                                                                 \
    (((dest) =* str), 1)                                                 \
    )

/* PF_JSON_ADVANCE_CHAR() macro:
   Incrementes str & tok->char_offset.
   For convenience of existing conditionals, returns the old value of c (0 on eof)
   Implicit inputs:  c var
*/
#define PF_JSON_ADVANCE_CHAR(str, tok) \
    ( ++(str), ((tok)->char_offset)++, c)

static const char* g_json_type_name[] = 
{
    /* If you change this, be sure to update the enum pf_json_type definition too */
    "null",
    "boolean",
    "double",
    "int",
    "object",
    "array",
    "string",
};

static const char* g_json_null_str = "null";
static const char* g_json_true_str = "true";
static const char* g_json_false_str = "false";
static int g_debug = 0;
static unsigned char g_utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD };

const char* g_json_number_chars = "0123456789.+-eE";
const char* g_json_hex_chars = "0123456789abcdef";
const char* g_json_tokener_errors[] = 
{
    "success",
    "continue",
    "nesting to deep",
    "unexpected end of data",
    "unexpected character",
    "null expected",
    "boolean expected",
    "number expected",
    "array value separator ',' expected",
    "quoted object property name expected",
    "object property name separator ':' expected",
    "object value separator ',' expected",
    "invalid string sequence",
    "expected comment",
};

/*********************************************************************************************
Function Name:  pf_json_escape_str
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_escape_str(struct print_buf* pb, char* str, int len);

/*********************************************************************************************
Function Name:  pf_json_obj_del_generic_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_obj_del_generic_handler(struct pf_json_object* jso);

/*********************************************************************************************
Function Name:  pf_json_create_obj_by_type
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static struct pf_json_object* pf_json_create_obj_by_type(enum pf_json_type o_type);

/*********************************************************************************************
Function Name:  pf_json_obj_to_str_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb);

/*********************************************************************************************
Function Name:  pf_json_obj_lh_entry_free_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_obj_lh_entry_free_handler(struct lh_entry* ent);

/*********************************************************************************************
Function Name:  pf_json_obj_del_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_obj_del_handler(struct pf_json_object* jso);

/*********************************************************************************************
Function Name:  pf_json_bool_obj_to_str_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_bool_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb);

/*********************************************************************************************
Function Name:  pf_json_int_obj_to_str_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_int_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb);

/*********************************************************************************************
Function Name:  pf_json_double_obj_to_str_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_double_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb);

/*********************************************************************************************
Function Name:  pf_json_str_obj_to_str_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_str_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb);

/*********************************************************************************************
Function Name:  pf_json_str_obj_del_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_str_obj_del_handler(struct pf_json_object* jso);

/*********************************************************************************************
Function Name:  pf_json_array_obj_to_str_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_array_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb);

/*********************************************************************************************
Function Name:  pf_json_obj_array_list_free_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_obj_array_list_free_handler(void* data);

/*********************************************************************************************
Function Name:  pf_json_array_obj_del_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_array_obj_del_handler(struct pf_json_object* jso);

/*********************************************************************************************
Function Name:  pf_json_reset_level_tokener
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_reset_level_tokener(struct pf_json_tokener* tok, int depth);

/*********************************************************************************************
Function Name:  print_buf_create
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static struct print_buf* print_buf_create(void);

/*********************************************************************************************
Function Name:  print_buf_mem_append
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int print_buf_mem_append(struct print_buf* p, const char* buf, int size);

/*********************************************************************************************
Function Name:  sprint_buf
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int sprint_buf(struct print_buf* p, const char* msg, ...);

/*********************************************************************************************
Function Name:  print_buf_reset
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void print_buf_reset(struct print_buf* p);

/*********************************************************************************************
Function Name:  print_buf_destroy
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void print_buf_destroy(struct print_buf* p);

/*********************************************************************************************
Function Name:  pf_json_abort
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_abort(const char* msg, ...);

/*********************************************************************************************
Function Name:  pf_json_debug
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_debug(const char* msg, ...);

/*********************************************************************************************
Function Name:  pf_json_error
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void pf_json_error(const char* msg, ...);

/*********************************************************************************************
Function Name:  lh_abort
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void lh_abort(const char* msg, ...);

/*********************************************************************************************
Function Name:  lh_char_hash_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static unsigned long lh_char_hash_handler(const void* k);

/*********************************************************************************************
Function Name:  lh_char_equal_handler
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int lh_char_equal_handler(const void* k1, const void* k2);

/*********************************************************************************************
Function Name:  lh_table_create
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static struct lh_table* lh_table_create(int size, 
                                        const char* name, 
                                        lh_entry_free_f *free_fn, 
                                        lh_hash_f *hash_fn, 
                                        lh_equal_f *equal_fn);

/*********************************************************************************************
Function Name:  lh_kchar_table_create
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static struct lh_table* lh_kchar_table_create(int size, 
                                              const char* name, 
                                              lh_entry_free_f *free_fn);

/*********************************************************************************************
Function Name:  lh_table_resize
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void lh_table_resize(struct lh_table* t, int new_size);

/*********************************************************************************************
Function Name:  lh_table_destroy
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void lh_table_destroy(struct lh_table* t);

/*********************************************************************************************
Function Name:  lh_table_insert
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int lh_table_insert(struct lh_table* t, void* k, const void* v);

/*********************************************************************************************
Function Name:  lh_table_lookup_entry
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static struct lh_entry* lh_table_lookup_entry(struct lh_table* t, const void* k);

/*********************************************************************************************
Function Name:  lh_table_lookup
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static const void* lh_table_lookup(struct lh_table* t, const void* k);

/*********************************************************************************************
Function Name:  lh_table_delete_entry
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int lh_table_delete_entry(struct lh_table* t, struct lh_entry* e);

/*********************************************************************************************
Function Name:  lh_table_delete
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int lh_table_delete(struct lh_table* t, const void* k);

/*********************************************************************************************
Function Name:  array_list_create
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static struct array_list* array_list_create(array_list_free_f* free_fn);

/*********************************************************************************************
Function Name:  array_list_destroy
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void array_list_destroy(struct array_list* arr);

/*********************************************************************************************
Function Name:  array_list_get_item
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static void* array_list_get_item(struct array_list* arr, int i);

/*********************************************************************************************
Function Name:  array_list_expand_internal
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int array_list_expand_internal(struct array_list* arr, int max);

/*********************************************************************************************
Function Name:  array_list_put_item
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int array_list_put_item(struct array_list* arr, int idx, void* data);

/*********************************************************************************************
Function Name:  array_list_add_item
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int array_list_add_item(struct array_list* arr, void* data);

/*********************************************************************************************
Function Name:  array_list_get_len
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int array_list_get_len(struct array_list* arr);

/*********************************************************************************************
Function Name:  pf_json_parse_int64
Description  :  
Inputs       :                          :   
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-08-24                    create
**********************************************************************************************/
static int pf_json_parse_int64(const char* buf, int64_t* retval);

/*********************************************************************************************/

pf_json_object_t* pf_json_get_obj(pf_json_object_t* jso)
{
    if(jso) 
    {
        jso->_ref_count++;
    }

    return jso;
}

void pf_json_put_obj(pf_json_object_t* jso)
{
    if(jso) 
    {
        jso->_ref_count--;
        if(!jso->_ref_count) 
        {
            jso->del_fn(jso);
        }
    }

    return;
}

int pf_json_is_obj_type(pf_json_object_t* jso, enum pf_json_type type)
{
    return (jso->o_type == type);
}

enum pf_json_type pf_json_get_obj_type(pf_json_object_t* jso)
{
    return jso->o_type;
}

/* pf_json_obj_to_json_str */
const char* pf_json_obj_to_json_str(pf_json_object_t* jso)
{
    if(!jso) 
    {
        return "null";
    }

    if(!jso->pb) 
    {
        if(!(jso->pb = print_buf_create())) 
        {
            return NULL;
        }
    } 
    else 
    {
        print_buf_reset(jso->pb);
    }
    if(jso->to_str_fn(jso, jso->pb) < 0) 
    {
        return NULL;
    }

    return jso->pb->buf;
}

pf_json_object_t* pf_json_create_obj(void)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_OBJECT);

    if(!jso) 
    {
        return NULL;
    }

    jso->del_fn = &pf_json_obj_del_handler;
    jso->to_str_fn = &pf_json_obj_to_str_handler;
    jso->o.c_object = lh_kchar_table_create(PF_JSON_OBJ_DEF_HASH_ENTRIES, 
                                            NULL, &pf_json_obj_lh_entry_free_handler);

    return jso;
}

struct lh_table* pf_json_obj_get_obj(pf_json_object_t* jso)
{
    if(!jso) 
    {
        return NULL;
    }

    switch(jso->o_type) 
    {
        case PF_JSON_TYPE_OBJECT:
            return jso->o.c_object;
        default:
            return NULL;
    }
}

void pf_json_add_sub_obj(pf_json_object_t* jso, const char* key, pf_json_object_t* val)
{
    lh_table_delete(jso->o.c_object, key);
    lh_table_insert(jso->o.c_object, strdup(key), val);

    return;
}

pf_json_object_t* pf_json_get_sub_obj(pf_json_object_t* jso, const char* key)
{
    if(!jso) 
    {
        return NULL;
    }

    return (pf_json_object_t*)lh_table_lookup(jso->o.c_object, key);
}

void pf_json_del_sub_obj(pf_json_object_t* jso, const char* key)
{
    lh_table_delete(jso->o.c_object, key);

    return;
}

int pf_json_obj_for_each(pf_json_object_t* obj, 
                         void* arg, 
                         int (*cb)(void* arg, char* key, pf_json_object_t* val))
{
    char*               key   = NULL;;
    pf_json_object_t*   val   = NULL;
    struct lh_entry*    entry = NULL;
    int                 ret   = 0;

    for(entry = pf_json_obj_get_obj(obj)->head; 
        (entry ? (key = (char*)entry->k, val = (pf_json_object_t*)entry->v, entry) : 0); 
        entry = entry->next)
    {
        if((ret = (*cb)(arg, key, val)) != 0)
        {
            return ret;
        }
    }

    return ret;
}

pf_json_object_t* pf_json_create_bool_obj(boolean b)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_BOOLEAN);

    if(!jso) 
    {
        return NULL;
    }
    jso->to_str_fn = &pf_json_bool_obj_to_str_handler;
    jso->o.c_boolean = b;

    return jso;
}

boolean pf_json_get_bool(pf_json_object_t* jso)
{
    if(!jso)
    {
        return FALSE;
    }

    switch(jso->o_type) 
    {
        case PF_JSON_TYPE_BOOLEAN:
            return jso->o.c_boolean;
        case PF_JSON_TYPE_INT:
            return (jso->o.c_int64 != 0);
        case PF_JSON_TYPE_DOUBLE:
            return (fabs(jso->o.c_double) >= DBL_EPSILON);
        case PF_JSON_TYPE_STRING:
            return (jso->o.c_string.len != 0);
        default:
            return FALSE;
    }
}

pf_json_object_t* pf_json_create_int_obj(int32_t i)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_INT);

    if(!jso)
    {
        return NULL;
    }

    jso->to_str_fn = &pf_json_int_obj_to_str_handler;
    jso->o.c_int64 = i;

    return jso;
}

int32_t pf_json_get_int(pf_json_object_t* jso)
{
    int64_t        cint64 = 0;
    enum pf_json_type o_type;

    if(!jso)
    {
        return 0;
    }

    o_type = jso->o_type;
    cint64 = jso->o.c_int64;

    if(o_type == PF_JSON_TYPE_STRING)
    {
        /*
        *         Parse strings into 64-bit numbers, then use the
        *         64-to-32-bit number handling below.
        */
        if(pf_json_parse_int64(jso->o.c_string.str, &cint64) != 0)
        {
            return 0; /* whoops, it didn't work. */
        }
        o_type = PF_JSON_TYPE_INT;
    }

    switch(o_type)
    {
        case PF_JSON_TYPE_INT:
            /* Make sure we return the correct values for out of range numbers. */
            if(cint64 <= INT32_MIN)
            {
                return INT32_MIN;
            }
            else if(cint64 >= INT32_MAX)
            {
                return INT32_MAX;
            }
            else
            {
                return (int32_t)cint64;
            }
        case PF_JSON_TYPE_DOUBLE:
            return (int32_t)jso->o.c_double;
        case PF_JSON_TYPE_BOOLEAN:
            return jso->o.c_boolean;
        default:
            return 0;
    }
}

pf_json_object_t* pf_json_create_int64_obj(int64_t i)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_INT);

    if(!jso)
    {
        return NULL;
    }

    jso->to_str_fn = &pf_json_int_obj_to_str_handler;
    jso->o.c_int64 = i;

    return jso;
}

int64_t pf_json_get_int64(pf_json_object_t* jso)
{
    int64_t cint;

    if(!jso)
    {
        return 0;
    }

    switch(jso->o_type)
    {
        case PF_JSON_TYPE_INT:
            return jso->o.c_int64;
        case PF_JSON_TYPE_DOUBLE:
            return (int64_t)jso->o.c_double;
        case PF_JSON_TYPE_BOOLEAN:
            return jso->o.c_boolean;
        case PF_JSON_TYPE_STRING:
            if(pf_json_parse_int64(jso->o.c_string.str, &cint) == 0)
            {
                return cint;
            }
        default:
            return 0;
    }
}

pf_json_object_t* pf_json_create_double_obj(double d)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_DOUBLE);

    if(!jso)
    {
        return NULL;
    }

    jso->to_str_fn = &pf_json_double_obj_to_str_handler;
    jso->o.c_double = d;

    return jso;
}

double pf_json_get_double(pf_json_object_t* jso)
{
    double cdouble;

    if(!jso)
    {
        return 0.0;
    }

    switch(jso->o_type)
    {
        case PF_JSON_TYPE_DOUBLE:
            return jso->o.c_double;
        case PF_JSON_TYPE_INT:
            return jso->o.c_int64;
        case PF_JSON_TYPE_BOOLEAN:
            return jso->o.c_boolean;
        case PF_JSON_TYPE_STRING:
            if(sscanf(jso->o.c_string.str, "%lf", &cdouble) == 1)
            {
                return cdouble;
            }
        default:
            return 0.0;
    }
}

pf_json_object_t* pf_json_create_str_obj(const char* s)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_STRING);

    if(!jso)
    {
        return NULL;
    }

    jso->del_fn = &pf_json_str_obj_del_handler;
    jso->to_str_fn = &pf_json_str_obj_to_str_handler;
    jso->o.c_string.str = strdup(s);
    jso->o.c_string.len = strlen(s);

    return jso;
}

pf_json_object_t* pf_json_create_str_len_obj(const char* s, int len)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_STRING);

    if(!jso)
    {
        return NULL;
    }

    jso->del_fn = &pf_json_str_obj_del_handler;
    jso->to_str_fn = &pf_json_str_obj_to_str_handler;
    jso->o.c_string.str = malloc(len + 1);
    jso->o.c_string.str[len] = 0;
    memcpy(jso->o.c_string.str, (void* )s, len);
    jso->o.c_string.len = len;

    return jso;
}

const char* pf_json_get_str(pf_json_object_t* jso)
{
    if(!jso)
    {
        return NULL;
    }

    switch(jso->o_type) 
    {
        case PF_JSON_TYPE_STRING:
            return jso->o.c_string.str;
        default:
            return pf_json_obj_to_json_str(jso);
    }
}

int pf_json_get_str_len(pf_json_object_t* jso)  
{
    if(!jso)
    {
        return 0;
    }

    switch(jso->o_type)
    {
          case PF_JSON_TYPE_STRING:
              return jso->o.c_string.len;
          default:
              return 0;
    }
}

pf_json_object_t* pf_json_create_array_obj(void)
{
    pf_json_object_t* jso = pf_json_create_obj_by_type(PF_JSON_TYPE_ARRAY);

    if(!jso)
    {
        return NULL;
    }

    jso->del_fn = &pf_json_array_obj_del_handler;
    jso->to_str_fn = &pf_json_array_obj_to_str_handler;
    jso->o.c_array = array_list_create(&pf_json_obj_array_list_free_handler);

    return jso;
}

int pf_json_array_obj_len(pf_json_object_t* jso)
{
    return array_list_get_len(jso->o.c_array);
}

int pf_json_array_obj_add_item(pf_json_object_t* jso,pf_json_object_t* val)
{
    return array_list_add_item(jso->o.c_array, val);
}

int pf_json_array_obj_put_item(pf_json_object_t* jso, int idx, pf_json_object_t* val)
{
    return array_list_put_item(jso->o.c_array, idx, val);
}

pf_json_object_t* pf_json_array_obj_get_item(pf_json_object_t* jso, int idx)
{
    return (pf_json_object_t*)array_list_get_item(jso->o.c_array, idx);
}

struct pf_json_tokener* pf_json_create_tokener(void)
{
    struct pf_json_tokener* tok = NULL;

    tok = (struct pf_json_tokener*)calloc(1, sizeof(struct pf_json_tokener));
    if(!tok)
    {
        return NULL;
    }
    tok->pb = print_buf_create();
    pf_json_reset_tokener(tok);

    return tok;
}

void pf_json_destroy_tokener(struct pf_json_tokener* tok)
{
    pf_json_reset_tokener(tok);
    if(tok)
    {
        print_buf_destroy(tok->pb);
    }
    free(tok);

    return;
}

void pf_json_reset_tokener(struct pf_json_tokener* tok)
{
    int i = 0;

    if(!tok)
    {
        return;
    }

    for(i = tok->depth; i >= 0; i--)
    {
        pf_json_reset_level_tokener(tok, i);
    }
    tok->depth = 0;
    tok->err = PF_JSON_TOK_SUCCESS;

    return;
}

pf_json_object_t* pf_json_parse(const char* str)
{
    struct pf_json_tokener* tok = NULL;
    pf_json_object_t*       obj = NULL;

    tok = pf_json_create_tokener();
    obj = pf_json_parse_ex(tok, str, -1);
    if(tok->err != PF_JSON_TOK_SUCCESS)
    {
        obj = NULL;
    }
    pf_json_destroy_tokener(tok);

    return obj;
}

pf_json_object_t* pf_json_parse_verbose(const char* str, enum pf_json_tokener_error* error)
{
    struct pf_json_tokener* tok = NULL;
    pf_json_object_t*       obj = NULL;;

    tok = pf_json_create_tokener();
    obj = pf_json_parse_ex(tok, str, -1);
    *error = tok->err;
    if(tok->err != PF_JSON_TOK_SUCCESS)
    {
        obj = NULL;
    }

    pf_json_destroy_tokener(tok);

    return obj;
}

pf_json_object_t* pf_json_parse_ex(struct pf_json_tokener* tok, const char* str, int len)
{
    unsigned char unescaped_utf[4] = {0};
    pf_json_object_t* obj  = NULL;
    char                c  = '\1';
    const char* case_start = NULL;
    unsigned int got_hi_surrogate = 0;
    int case_len = 0;
    int64_t num64;
    double  numd;

    tok->char_offset = 0;
    tok->err = PF_JSON_TOK_SUCCESS;

    while (PF_JSON_POP_CHAR(c, tok)) 
    {
redo_char:
        switch(PF_JSON_TOKENER_STATE) 
        {
            case PF_JSON_TOK_STATE_EATWS:
                /* Advance until we change state */
                while (isspace((int)c)) 
                {
                    if((!PF_JSON_ADVANCE_CHAR(str, tok)) || (!PF_JSON_POP_CHAR(c, tok)))
                    {
                        goto out;
                    }
                }
                if(c == '/')
                {
                    print_buf_reset(tok->pb);
                    PRINT_BUF_MEM_APPEND_FAST(tok->pb, &c, 1);
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_COMMENT_START;
                }
                else
                {
                    PF_JSON_TOKENER_STATE = PF_JSON_TOKENER_SAVED_STATE;
                    goto redo_char;
                }
                break;

            case PF_JSON_TOK_STATE_START:
                switch(c)
                {
                    case '{':
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                        PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_OBJECT_FIELD_START;
                        PF_JSON_TOKENER_CURRENT = pf_json_create_obj();
                        break;
                    case '[':
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                        PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_ARRAY;
                        PF_JSON_TOKENER_CURRENT = pf_json_create_array_obj();
                        break;
                    case 'N':
                    case 'n':
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_NULL;
                        print_buf_reset(tok->pb);
                        tok->st_pos = 0;
                        goto redo_char;
                    case '"':
                    case '\'':
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_STRING;
                        print_buf_reset(tok->pb);
                        tok->quote_char = c;
                        break;
                    case 'T':
                    case 't':
                    case 'F':
                    case 'f':
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_BOOLEAN;
                        print_buf_reset(tok->pb);
                        tok->st_pos = 0;
                        goto redo_char;
                    #if defined(__GNUC__)
                    case '0' ... '9':
                    #else
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                    #endif
                    case '-':
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_NUMBER;
                        print_buf_reset(tok->pb);
                        tok->is_double = 0;
                        goto redo_char;
                    default:
                        tok->err = PF_JSON_TOK_ERR_PARSE_UNEXPECTED;
                        goto out;
                }
                break;

            case PF_JSON_TOK_STATE_FINISH:
                if(tok->depth == 0)
                {
                    goto out;
                }
                obj = pf_json_get_obj(PF_JSON_TOKENER_CURRENT);
                pf_json_reset_level_tokener(tok, tok->depth);
                tok->depth--;
                goto redo_char;

            case PF_JSON_TOK_STATE_NULL:
                PRINT_BUF_MEM_APPEND_FAST(tok->pb, &c, 1);
                if(strncasecmp(g_json_null_str, tok->pb->buf, 
                               PF_JSON_MIN(tok->st_pos + 1, (signed)strlen(g_json_null_str))) == 0)
                {
                        if(tok->st_pos == (signed)strlen(g_json_null_str))
                        {
                            PF_JSON_TOKENER_CURRENT = NULL;
                            PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                            PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                            goto redo_char;
                        }
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_NULL;
                    goto out;
                }
                tok->st_pos++;
                break;

            case PF_JSON_TOK_STATE_COMMENT_START:
                if(c == '*')
                {
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_COMMENT;
                }
                else if(c == '/')
                {
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_COMMENT_EOL;
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_COMMENT;
                    goto out;
                }
                PRINT_BUF_MEM_APPEND_FAST(tok->pb, &c, 1);
                break;

            case PF_JSON_TOK_STATE_COMMENT:
                /* Advance until we change state */
                case_start = str;

                while(c != '*')
                {
                    if(!PF_JSON_ADVANCE_CHAR(str, tok) || !PF_JSON_POP_CHAR(c, tok))
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str - case_start);
                        goto out;
                    } 
                }
                PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, 1 + str - case_start);
                PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_COMMENT_END;
                break;

            case PF_JSON_TOK_STATE_COMMENT_EOL:
                /* Advance until we change state */
                case_start = str;

                while(c != '\n')
                {
                    if(!PF_JSON_ADVANCE_CHAR(str, tok) || !PF_JSON_POP_CHAR(c, tok))
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str - case_start);
                        goto out;
                    }
                }
                PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str - case_start);
                PF_JSON_DEBUG("json_tokener_comment: %s\n", tok->pb->buf);
                PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                break;

            case PF_JSON_TOK_STATE_COMMENT_END:
                PRINT_BUF_MEM_APPEND_FAST(tok->pb, &c, 1);
                if(c == '/')
                {
                    PF_JSON_DEBUG("json_tokener_comment: %s\n", tok->pb->buf);
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else
                {
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_COMMENT;
                }
                break;

            case PF_JSON_TOK_STATE_STRING:
                /* Advance until we change state */
                case_start = str;

                while(1)
                {
                    if(c == tok->quote_char)
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str - case_start);
                        PF_JSON_TOKENER_CURRENT = pf_json_create_str_obj(tok->pb->buf);
                        PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                        break;
                    }
                    else if(c == '\\')
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str - case_start);
                        PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_STRING;
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_STRING_ESCAPE;
                        break;
                    }
                    if(!PF_JSON_ADVANCE_CHAR(str, tok) || !PF_JSON_POP_CHAR(c, tok))
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str - case_start);
                        goto out;
                    }
                }
                break;

            case PF_JSON_TOK_STATE_STRING_ESCAPE:
                switch(c)
                {
                    case '"':
                    case '\\':
                    case '/':
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, &c, 1);
                        PF_JSON_TOKENER_STATE = PF_JSON_TOKENER_SAVED_STATE;
                        break;
                    case 'b':
                    case 'n':
                    case 'r':
                    case 't':
                        if(c == 'b')
                        {
                            PRINT_BUF_MEM_APPEND_FAST(tok->pb, "\b", 1);
                        }
                        else if(c == 'n')
                        {
                            PRINT_BUF_MEM_APPEND_FAST(tok->pb, "\n", 1);
                        }
                        else if(c == 'r')
                        {
                            PRINT_BUF_MEM_APPEND_FAST(tok->pb, "\r", 1);
                        }
                        else if(c == 't')
                        {
                            PRINT_BUF_MEM_APPEND_FAST(tok->pb, "\t", 1);
                        }
                        PF_JSON_TOKENER_STATE = PF_JSON_TOKENER_SAVED_STATE;
                        break;
                    case 'u':
                        tok->ucs_char = 0;
                        tok->st_pos = 0;
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_ESCAPE_UNICODE;
                        break;
                    default:
                        tok->err = PF_JSON_TOK_ERR_PARSE_STRING;
                        goto out;
                }
                break;

            case PF_JSON_TOK_STATE_ESCAPE_UNICODE:
                got_hi_surrogate = 0;

                /* Handle a 4-byte sequence, or two sequences if a surrogate pair */
                while(1)
                {
                    if(strchr(g_json_hex_chars, c))
                    {
                        tok->ucs_char += ((unsigned int)HEXDIGIT(c) 
                                          << ((3 - tok->st_pos++) * 4));
                        if(tok->st_pos == 4)
                        {
                            if(got_hi_surrogate)
                            {
                                if(IS_LOW_SURROGATE(tok->ucs_char))
                                {
                                    /* Recalculate the ucs_char, then fall thru to process normally */
                                    tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, 
                                                                          tok->ucs_char);
                                }
                                else
                                {
                                    /* Hi surrogate was not followed by a low surrogate */
                                    /* Replace the hi and process the rest normally */
                                    PRINT_BUF_MEM_APPEND_FAST(tok->pb, 
                                                              (char*)g_utf8_replacement_char, 3);
                                }
                                got_hi_surrogate = 0;
                            }

                            if(tok->ucs_char < 0x80)
                            {
                                unescaped_utf[0] = tok->ucs_char;
                                PRINT_BUF_MEM_APPEND_FAST(tok->pb, (char*)unescaped_utf, 1);
                            }
                            else if(tok->ucs_char < 0x800)
                            {
                                unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6);
                                unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f);
                                PRINT_BUF_MEM_APPEND_FAST(tok->pb, (char*)unescaped_utf, 2);
                            }
                            else if(IS_HIGH_SURROGATE(tok->ucs_char))
                            {
                                /* Got a high surrogate.  
                                *  Remember it and look for the
                                *  the beginning of another sequence, which should be the
                                *  low surrogate.
                                */
                                got_hi_surrogate = tok->ucs_char;
                                /* Not at end, and the next two chars should be "\u" */
                                if((tok->char_offset+1 != len) 
                                   && (tok->char_offset+2 != len) 
                                   && (str[1] == '\\') && (str[2] == 'u'))
                                {
                                    PF_JSON_ADVANCE_CHAR(str, tok);
                                    PF_JSON_ADVANCE_CHAR(str, tok);
                                    /* Advance to the first char of the next sequence and
                                    *  continue processing with the next sequence.
                                    */
                                    if(!PF_JSON_ADVANCE_CHAR(str, tok) 
                                       || !PF_JSON_POP_CHAR(c, tok))
                                    {
                                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, 
                                                                  (char*)g_utf8_replacement_char, 3);
                                        goto out;
                                    }
                                    tok->ucs_char = 0;
                                    tok->st_pos = 0;
                                    continue; /* other PF_JSON_TOK_STATE_ESCAPE_UNICODE */
                                }
                                else
                                {
                                    /* Got a high surrogate without another sequence following
                                    *  it.  Put a replacement char in for the hi surrogate
                                    *  and pretend we finished.
                                    */
                                    PRINT_BUF_MEM_APPEND_FAST(tok->pb, 
                                                              (char*)g_utf8_replacement_char, 3);
                                }
                            }
                            else if(IS_LOW_SURROGATE(tok->ucs_char))
                            {
                                /* Got a low surrogate not preceded by a high */
                                PRINT_BUF_MEM_APPEND_FAST(tok->pb, 
                                                          (char*)g_utf8_replacement_char, 3);
                            }
                            else if(tok->ucs_char < 0x10000)
                            {
                                unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12);
                                unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f);
                                unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f);
                                PRINT_BUF_MEM_APPEND_FAST(tok->pb, (char*)unescaped_utf, 3);
                            }
                            else if(tok->ucs_char < 0x110000)
                            {
                                unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07);
                                unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f);
                                unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f);
                                unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f);
                                PRINT_BUF_MEM_APPEND_FAST(tok->pb, (char*)unescaped_utf, 4);
                            }
                            else
                            {
                                /* Don't know what we got--insert the replacement char */
                                PRINT_BUF_MEM_APPEND_FAST(tok->pb, 
                                                          (char*)g_utf8_replacement_char, 3);
                            }
                            PF_JSON_TOKENER_STATE = PF_JSON_TOKENER_SAVED_STATE;
                            break;
                        }
                    }
                    else
                    {
                        tok->err = PF_JSON_TOK_ERR_PARSE_STRING;
                        goto out;
                    }
                    if(!PF_JSON_ADVANCE_CHAR(str, tok) || !PF_JSON_POP_CHAR(c, tok))
                    {
                        if(got_hi_surrogate) /* Clean up any pending chars */
                        {
                            PRINT_BUF_MEM_APPEND_FAST(tok->pb, (char*)g_utf8_replacement_char, 3);
                        }
                        goto out;
                    }
                } /* while(1) */
                break;

            case PF_JSON_TOK_STATE_BOOLEAN:
                PRINT_BUF_MEM_APPEND_FAST(tok->pb, &c, 1);
                if(strncasecmp(g_json_true_str, tok->pb->buf, 
                               PF_JSON_MIN(tok->st_pos+1, (signed)strlen(g_json_true_str))) == 0)
                {
                        if(tok->st_pos == (signed)strlen(g_json_true_str))
                        {
                            PF_JSON_TOKENER_CURRENT = pf_json_create_bool_obj(1);
                            PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                            PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                            goto redo_char;
                        }
                }
                else if(strncasecmp(g_json_false_str, tok->pb->buf, 
                                    PF_JSON_MIN(tok->st_pos+1, 
                                                (signed)strlen(g_json_false_str))) == 0)
                {
                        if(tok->st_pos == (signed)strlen(g_json_false_str))
                        {
                            PF_JSON_TOKENER_CURRENT = pf_json_create_bool_obj(0);
                            PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                            PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                            goto redo_char;
                        }
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_BOOLEAN;
                    goto out;
                }
                tok->st_pos++;
                break;

            case PF_JSON_TOK_STATE_NUMBER:
                /* Advance until we change state */
                case_start = str;
                case_len = 0;

                while(c && strchr(g_json_number_chars, c))
                {
                    ++case_len;
                    if(c == '.' || c == 'e') tok->is_double = 1;
                    if(!PF_JSON_ADVANCE_CHAR(str, tok) || !PF_JSON_POP_CHAR(c, tok))
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, case_len);
                        goto out;
                    }
                }
                if(case_len > 0)
                {
                    PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, case_len);
                }

                if(!tok->is_double && pf_json_parse_int64(tok->pb->buf, &num64) == 0)
                {
                    PF_JSON_TOKENER_CURRENT = pf_json_create_int64_obj(num64);
                }
                else if(tok->is_double && sscanf(tok->pb->buf, "%lf", &numd) == 1)
                {
                    PF_JSON_TOKENER_CURRENT = pf_json_create_double_obj(numd);
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_NUMBER;
                    goto out;
                }
                PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                goto redo_char;

            case PF_JSON_TOK_STATE_ARRAY:
                if(c == ']')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                } 
                else 
                {
                    if(tok->depth >= PF_JSON_TOKENER_MAX_DEPTH - 1)
                    {
                        tok->err = PF_JSON_TOK_ERR_DEPTH;
                        goto out;
                    }
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_ARRAY_ADD;
                    tok->depth++;
                    pf_json_reset_level_tokener(tok, tok->depth);
                    goto redo_char;
                }
                break;

            case PF_JSON_TOK_STATE_ARRAY_ADD:
                pf_json_array_obj_add_item(PF_JSON_TOKENER_CURRENT, obj);
                PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_ARRAY_SEP;
                PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                goto redo_char;

            case PF_JSON_TOK_STATE_ARRAY_SEP:
                if(c == ']')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else if(c == ',')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_ARRAY;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_ARRAY;
                    goto out;
                }
                break;

            case PF_JSON_TOK_STATE_OBJECT_FIELD_START:
                if(c == '}')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else if(c == '"' || c == '\'')
                {
                    tok->quote_char = c;
                    print_buf_reset(tok->pb);
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_OBJECT_FIELD;
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_OBJECT_KEY_NAME;
                    goto out;
                }
                break;

            case PF_JSON_TOK_STATE_OBJECT_FIELD:
                /* Advance until we change state */
                case_start = str;

                while(1)
                {
                    if(c == tok->quote_char)
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str-case_start);
                        PF_JSON_TOKENER_OBJ_FIELD_NAME = strdup(tok->pb->buf);
                        PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_OBJECT_FIELD_END;
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                        break;
                    }
                    else if(c == '\\')
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str-case_start);
                        PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_OBJECT_FIELD;
                        PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_STRING_ESCAPE;
                        break;
                    }
                    if(!PF_JSON_ADVANCE_CHAR(str, tok) || !PF_JSON_POP_CHAR(c, tok))
                    {
                        PRINT_BUF_MEM_APPEND_FAST(tok->pb, case_start, str-case_start);
                        goto out;
                    }
                }
                break;

            case PF_JSON_TOK_STATE_OBJECT_FIELD_END:
                if(c == ':')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_OBJECT_VALUE;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_OBJECT_KEY_SEP;
                    goto out;
                }
                break;

            case PF_JSON_TOK_STATE_OBJECT_VALUE:
                if(tok->depth >= PF_JSON_TOKENER_MAX_DEPTH-1)
                {
                    tok->err = PF_JSON_TOK_ERR_DEPTH;
                    goto out;
                }
                PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_OBJECT_VALUE_ADD;
                tok->depth++;
                pf_json_reset_level_tokener(tok, tok->depth);
                goto redo_char;

            case PF_JSON_TOK_STATE_OBJECT_VALUE_ADD:
                pf_json_add_sub_obj(PF_JSON_TOKENER_CURRENT, PF_JSON_TOKENER_OBJ_FIELD_NAME, obj);
                free(PF_JSON_TOKENER_OBJ_FIELD_NAME);
                PF_JSON_TOKENER_OBJ_FIELD_NAME = NULL;
                PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_OBJECT_SEP;
                PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                goto redo_char;

            case PF_JSON_TOK_STATE_OBJECT_SEP:
                if(c == '}')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_FINISH;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else if(c == ',')
                {
                    PF_JSON_TOKENER_SAVED_STATE = PF_JSON_TOK_STATE_OBJECT_FIELD_START;
                    PF_JSON_TOKENER_STATE = PF_JSON_TOK_STATE_EATWS;
                }
                else
                {
                    tok->err = PF_JSON_TOK_ERR_PARSE_OBJECT_VALUE_SEP;
                    goto out;
                }
                break;

        }
        if(!PF_JSON_ADVANCE_CHAR(str, tok))
        {
            goto out;
        }
    } /* while(PF_JSON_POP_CHAR) */

out:
    if(!c)
    { /* We hit an eof char (0) */
        if(PF_JSON_TOKENER_STATE != PF_JSON_TOK_STATE_FINISH 
           && PF_JSON_TOKENER_SAVED_STATE != PF_JSON_TOK_STATE_FINISH)
        {
            tok->err = PF_JSON_TOK_ERR_PARSE_EOF;
        }
    }

    if(tok->err == PF_JSON_TOK_SUCCESS)
    {
        return pf_json_get_obj(PF_JSON_TOKENER_CURRENT);
    }
    PF_JSON_DEBUG("pf_json_parse_ex: error %s at offset %d\n", 
                  g_json_tokener_errors[tok->err], tok->char_offset);

    return NULL;
}

void pf_json_set_debug(int debug)
{
    g_debug = debug; 
}

int pf_json_get_debug(void)
{
    return g_debug; 
}

pf_json_object_t* pf_json_create_obj_from_file(const char* filename)
{
    struct print_buf*    pb          = NULL;;
    pf_json_object_t*    obj         = NULL;
    char buf[PF_JSON_FILE_BUF_SIZE]  = {0,};
    int                  fd          = 0; 
    int                  ret         = 0;

    if((fd = open(filename, O_RDONLY)) < 0)
    {
        PF_JSON_ERROR("pf_json_create_obj_from_file: error reading file %s: %s\n", 
                      filename, strerror(errno));
        return NULL;
    }
    if(!(pb = print_buf_create()))
    {
        close(fd);
        PF_JSON_ERROR("pf_json_create_obj_from_file: print_buf_create failed\n");
        return NULL;
    }
    while((ret = read(fd, buf, PF_JSON_FILE_BUF_SIZE)) > 0)
    {
        print_buf_mem_append(pb, buf, ret);
    }
    close(fd);
    if(ret < 0)
    {
        PF_JSON_ABORT("pf_json_create_obj_from_file: error reading file %s: %s\n", 
                      filename, strerror(errno));
        print_buf_destroy(pb);
        return NULL;
    }
    obj = pf_json_parse(pb->buf);
    print_buf_destroy(pb);

    return obj;
}

int pf_json_obj_to_file(char* filename, pf_json_object_t* obj)
{
    const char*   json_str = NULL;
    int           fd       = 0;
    int           ret      = 0;
    unsigned int  wpos     = 0;
    unsigned int  wsize    = 0;

    if(!obj)
    {
        PF_JSON_ERROR("pf_json_obj_to_file: object is null\n");
        return -1;
    }

    if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0)
    {
        PF_JSON_ERROR("pf_json_obj_to_file: error opening file %s: %s\n", 
                      filename, strerror(errno));
        return -1;
    }

    if(!(json_str = pf_json_obj_to_json_str(obj)))
    {
        close(fd);
        return -1;
    }

    /* CAW: probably unnecessary, but the most 64bit safe */
    wsize = (unsigned int)(strlen(json_str) & UINT_MAX); 

    wpos = 0;
    while(wpos < wsize)
    {
        if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0)
        {
            close(fd);
            PF_JSON_ERROR("pf_json_obj_to_file: error writing file %s: %s\n", 
                          filename, strerror(errno));
            return -1;
        }

        /* because of the above check for ret < 0, we can safely cast and add */
        wpos += (unsigned int)ret;
    }

    close(fd);

    return 0;
}

const char* pf_json_obj_type_to_name(enum pf_json_type o_type)
{
    if(o_type >= PF_ARR_SIZE(g_json_type_name))
    {
        PF_JSON_ERROR("pf_json_obj_type_to_name: type %d is out of range [0,%d]\n",
                      o_type, PF_ARR_SIZE(g_json_type_name));
        return NULL;
    }

    return g_json_type_name[o_type];
}

/*********************************************************************************************/

/* string escaping */
static int pf_json_escape_str(struct print_buf* pb, char* str, int len)
{
    int pos          = 0;
    int start_offset = 0;
    unsigned char c;
    while (len--)
    {
        c = str[pos];
        switch(c)
        {
            case '\b':
            case '\n':
            case '\r':
            case '\t':
            case '"':
            case '\\':
            case '/':
                if(pos - start_offset > 0)
                {
                    print_buf_mem_append(pb, str + start_offset, pos - start_offset);
                }
                if(c == '\b')
                {
                    print_buf_mem_append(pb, "\\b", 2);
                }
                else if(c == '\n')
                {
                    print_buf_mem_append(pb, "\\n", 2);
                }
                else if(c == '\r')
                {
                    print_buf_mem_append(pb, "\\r", 2);
                }
                else if(c == '\t')
                {
                    print_buf_mem_append(pb, "\\t", 2);
                }
                else if(c == '"')
                {
                    print_buf_mem_append(pb, "\\\"", 2);
                }
                else if(c == '\\')
                {
                    print_buf_mem_append(pb, "\\\\", 2);
                }
                else if(c == '/')
                {
                    print_buf_mem_append(pb, "\\/", 2);
                }
                start_offset = ++pos;
                break;
            default:
                if(c < ' ')
                {
                    if(pos - start_offset > 0)
                    {
                        print_buf_mem_append(pb, str + start_offset, pos - start_offset);
                    }
                    sprint_buf(pb, "\\u00%c%c", 
                               g_json_hex_chars[c >> 4], g_json_hex_chars[c & 0xf]);
                    start_offset = ++pos;
                }
                else
                {
                    pos++;
                }
                break;
        }
    }
    if(pos - start_offset > 0)
    {
        print_buf_mem_append(pb, str + start_offset, pos - start_offset);
    }

    return 0;
}

/* generic object construction and destruction parts */
static void pf_json_obj_del_generic_handler(struct pf_json_object* jso)
{
    print_buf_destroy(jso->pb);
    free(jso);

    return;
}

static struct pf_json_object* pf_json_create_obj_by_type(enum pf_json_type o_type)
{
    struct pf_json_object* jso = NULL;

    jso = (struct pf_json_object*)calloc(sizeof(struct pf_json_object), 1);
    if(!jso)
    {
        return NULL;
    }
    jso->o_type = o_type;
    jso->_ref_count = 1;
    jso->del_fn = &pf_json_obj_del_generic_handler;

    return jso;
}

/* json_object_object */
static int pf_json_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb)
{
    int i = 0;
    struct pf_json_object_iter iter;

    sprint_buf(pb, "{");

    /* CAW: scope operator to make ANSI correctness */
    /* CAW: switched to pf_json_obj_for_each_obj which uses an iterator struct */
    pf_json_obj_for_each_obj(jso, iter)
    {
        if(i)
        {
            sprint_buf(pb, ",");
        }
        sprint_buf(pb, " \"");
        pf_json_escape_str(pb, iter.key, strlen(iter.key));
        sprint_buf(pb, "\": ");
        if(iter.val == NULL)
        {
            sprint_buf(pb, "null");
        }
        else
        {
            iter.val->to_str_fn(iter.val, pb);
        }
        i++;
    }

    return sprint_buf(pb, " }");
}

static void pf_json_obj_lh_entry_free_handler(struct lh_entry* ent)
{
    free(ent->k);
    pf_json_put_obj((struct pf_json_object*)ent->v);

    return; 
}

static void pf_json_obj_del_handler(struct pf_json_object* jso)
{
    lh_table_destroy(jso->o.c_object);
    pf_json_obj_del_generic_handler(jso);

    return;
}

/* json_object_boolean */
static int pf_json_bool_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb)
{
    if(jso->o.c_boolean)
    {
        return sprint_buf(pb, "true");
    }
    else
    {
        return sprint_buf(pb, "false");
    }
}

/* json_object_int */
static int pf_json_int_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb)
{
    return sprint_buf(pb, "%"PRId64, jso->o.c_int64);
}


/* json_object_double */
static int pf_json_double_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb)
{
    return sprint_buf(pb, "%lf", jso->o.c_double);
}

/* json_object_string */
static int pf_json_str_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb)
{
    sprint_buf(pb, "\"");
    pf_json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len);
    sprint_buf(pb, "\"");
    return 0;
}

static void pf_json_str_obj_del_handler(struct pf_json_object* jso)
{
    free(jso->o.c_string.str);
    pf_json_obj_del_generic_handler(jso);
}

/* json_object_array */
static int pf_json_array_obj_to_str_handler(struct pf_json_object* jso, struct print_buf* pb)
{
    int i = 0;
    struct pf_json_object* val = NULL;

    sprint_buf(pb, "[");
    for(i=0; i < pf_json_array_obj_len(jso); i++)
    {
        if(i)
        { 
            sprint_buf(pb, ", "); 
        }
        else
        { 
            sprint_buf(pb, " "); 
        }

        val = pf_json_array_obj_get_item(jso, i);
        if(val == NULL)
        { 
            sprint_buf(pb, "null"); 
        }
        else
        { 
            val->to_str_fn(val, pb); 
        }
    }

    return sprint_buf(pb, " ]");
}

static void pf_json_obj_array_list_free_handler(void* data)
{
    pf_json_put_obj((struct pf_json_object*)data);

    return;
}

static void pf_json_array_obj_del_handler(struct pf_json_object* jso)
{
    array_list_destroy(jso->o.c_array);
    pf_json_obj_del_generic_handler(jso);

    return;
}

static void pf_json_reset_level_tokener(struct pf_json_tokener* tok, int depth)
{
    tok->stack[depth].state = PF_JSON_TOK_STATE_EATWS;
    tok->stack[depth].saved_state = PF_JSON_TOK_STATE_START;
    pf_json_put_obj(tok->stack[depth].current);
    tok->stack[depth].current = NULL;
    free(tok->stack[depth].obj_field_name);
    tok->stack[depth].obj_field_name = NULL;

    return;
}

/* print_buf.c */
static struct print_buf* print_buf_create(void)
{
    struct print_buf* p = NULL;

    p = (struct print_buf*)calloc(1, sizeof(struct print_buf));
    if(!p)
    {
        return NULL;
    }
    p->size = 32;
    p->bpos = 0;
    if(!(p->buf = (char*)malloc(p->size)))
    {
        free(p);
        return NULL;
    }

    return p;
}

static int print_buf_mem_append(struct print_buf* p, const char* buf, int size)
{
    char* t      = NULL;
    int new_size = -1;

    if(p->size - p->bpos <= size)
    {
        new_size = PF_JSON_MAX(p->size*  2, p->bpos + size + 8);
        if(!(t = (char*)realloc(p->buf, new_size)))
        {
            return -1;
        }
        p->size = new_size;
        p->buf = t;
    }
    memcpy(p->buf + p->bpos, buf, size);
    p->bpos += size;
    p->buf[p->bpos]= '\0';

    return size;
}

static int sprint_buf(struct print_buf* p, const char* msg, ...)
{
    va_list ap;
    char* t;
    int size;
    char buf[128];

    /* user stack buffer first */
    va_start(ap, msg);
    size = vsnprintf(buf, 128, msg, ap);
    va_end(ap);
    /* if string is greater than stack buffer, then use dynamic string
    with vasprintf.  Note: some implementation of vsnprintf return -1
    if output is truncated whereas some return the number of bytes that
    would have been written - this code handles both cases. */
    if(size == -1 || size > 127)
    {
        va_start(ap, msg);
        if((size = vasprintf(&t, msg, ap)) < 0)
        { 
            va_end(ap); return -1; 
        }
        va_end(ap);
        print_buf_mem_append(p, t, size);
        free(t);
        return size;
    }
    else
    {
        print_buf_mem_append(p, buf, size);
        return size;
    }
}

static void print_buf_reset(struct print_buf* p)
{
    p->buf[0] = '\0';
    p->bpos = 0;

    return;
}

static void print_buf_destroy(struct print_buf* p)
{
    if(p)
    {
        free(p->buf);
        free(p);
    }

    return;
}

static void pf_json_abort(const char* msg, ...)
{
    va_list ap;

    va_start(ap, msg);
    vprintf(msg, ap);
    va_end(ap);

    exit(1);
}

static void pf_json_debug(const char* msg, ...)
{
    va_list ap;

    if(g_debug)
    {
        va_start(ap, msg);
        vprintf(msg, ap);
        va_end(ap);
    }

    return;
}

static void pf_json_error(const char* msg, ...)
{
    va_list ap;

    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);

    return;
}

static void lh_abort(const char* msg, ...)
{
    va_list ap;

    va_start(ap, msg);
    vprintf(msg, ap);
    va_end(ap);

    exit(1);
}

static unsigned long lh_char_hash_handler(const void* k)
{
    unsigned int h   = 0;
    const char* data = (const char*)k;

    while(*data != 0)
    {
        h = h * 129 + (unsigned int)(*data++) + LH_PRIME;
    }

    return h;
}

static int lh_char_equal_handler(const void* k1, const void* k2)
{
    return (strcmp((const char*)k1, (const char*)k2) == 0);
}

static struct lh_table* lh_table_create(int size, 
                                        const char* name,
                                        lh_entry_free_f* free_fn,
                                        lh_hash_f* hash_fn,
                                        lh_equal_f* equal_fn)
{
    int i;
    struct lh_table* t;

    t = (struct lh_table*)calloc(1, sizeof(struct lh_table));
    if(!t)
    {
        lh_abort("lh_table_create: calloc failed\n");
    }
    t->count = 0;
    t->size = size;
    t->name = name;
    t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry));
    if(!t->table)
    {
        lh_abort("lh_table_create: calloc failed\n");
    }
    t->free_fn = free_fn;
    t->hash_fn = hash_fn;
    t->equal_fn = equal_fn;
    for(i = 0; i < size; i++)
    {
        t->table[i].k = LH_EMPTY;
    }

    return t;
}

static struct lh_table* lh_kchar_table_create(int size, 
                                              const char* name, 
                                              lh_entry_free_f* free_fn)
{
    return lh_table_create(size, name, free_fn, lh_char_hash_handler, lh_char_equal_handler);
}

static void lh_table_resize(struct lh_table* t, int new_size)
{
    struct lh_table* new_t;
    struct lh_entry* ent;

    new_t = lh_table_create(new_size, t->name, NULL, t->hash_fn, t->equal_fn);
    ent = t->head;
    while(ent)
    {
        lh_table_insert(new_t, ent->k, ent->v);
        ent = ent->next;
    }
    free(t->table);
    t->table = new_t->table;
    t->size = new_size;
    t->head = new_t->head;
    t->tail = new_t->tail;
    t->resizes++;
    free(new_t);

    return;
}

static void lh_table_destroy(struct lh_table* t)
{
    struct lh_entry* c;

    for(c = t->head; c != NULL; c = c->next)
    {
        if(t->free_fn)
        {
            t->free_fn(c);
        }
    }
    free(t->table);
    free(t);

    return;
}

static int lh_table_insert(struct lh_table* t, void* k, const void* v)
{
    unsigned long h, n;

    t->inserts++;
    if(t->count > t->size * 0.66)
    {
        lh_table_resize(t, t->size * 2);
    }

    h = t->hash_fn(k);
    n = h % t->size;

    while(1)
    {
        if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED)
        {
            break;
        }
        t->collisions++;
        if(++n == (unsigned long)t->size)
        {
            n = 0;
        }
    }

    t->table[n].k = k;
    t->table[n].v = v;
    t->count++;

    if(t->head == NULL)
    {
        t->head = t->tail = &t->table[n];
        t->table[n].next = t->table[n].prev = NULL;
    }
    else
    {
        t->tail->next = &t->table[n];
        t->table[n].prev = t->tail;
        t->table[n].next = NULL;
        t->tail = &t->table[n];
    }

    return 0;
}

static struct lh_entry* lh_table_lookup_entry(struct lh_table* t, const void* k)
{
    unsigned long h = t->hash_fn(k);
    unsigned long n = h % t->size;
    int count       = 0;

    t->lookups++;
    while(count < t->size)
    {
        if(t->table[n].k == LH_EMPTY)
        {
            return NULL;
        }
        if(t->table[n].k != LH_FREED && t->equal_fn(t->table[n].k, k))
        {
            return &t->table[n];
        }
        if(++n == (unsigned long)t->size)
        {
            n = 0;
        }
        count++;
    }

    return NULL;
}

static const void* lh_table_lookup(struct lh_table* t, const void* k)
{
    struct lh_entry* e = lh_table_lookup_entry(t, k);

    if(e)
    {
        return e->v;
    }

    return NULL;
}

static int lh_table_delete_entry(struct lh_table* t, struct lh_entry* e)
{
    /* CAW: fixed to be 64bit nice, still need the crazy negative case... */
    ptrdiff_t n = (ptrdiff_t)(e - t->table); 

    /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */
    if(n < 0)
    { 
        return -2; 
    }

    if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED)
    {
        return -1;
    }
    t->count--;
    if(t->free_fn)
    {
        t->free_fn(e);
    }
    t->table[n].v = NULL;
    t->table[n].k = LH_FREED;
    if(t->tail == &t->table[n] && t->head == &t->table[n])
    {
        t->head = t->tail = NULL;
    }
    else if(t->head == &t->table[n])
    {
        t->head->next->prev = NULL;
        t->head = t->head->next;
    }
    else if(t->tail == &t->table[n])
    {
        t->tail->prev->next = NULL;
        t->tail = t->tail->prev;
    }
    else
    {
        t->table[n].prev->next = t->table[n].next;
        t->table[n].next->prev = t->table[n].prev;
    }
    t->table[n].next = t->table[n].prev = NULL;

    return 0;
}

static int lh_table_delete(struct lh_table* t, const void* k)
{
    struct lh_entry* e = lh_table_lookup_entry(t, k);

    if(!e)
    {
        return -1;
    }

    return lh_table_delete_entry(t, e);
}

static struct array_list* array_list_create(array_list_free_f* free_fn)
{
    struct array_list* arr;

    arr = (struct array_list*)calloc(1, sizeof(struct array_list));
    if(!arr)
    {
        return NULL;
    }
    arr->size = PF_JSON_ARRAY_LIST_DEF_SIZE;
    arr->length = 0;
    arr->free_fn = free_fn;
    if(!(arr->array = (void**)calloc(sizeof(void*), arr->size)))
    {
        free(arr);
        return NULL;
    }

    return arr;
}

static void array_list_destroy(struct array_list* arr)
{
    int i;

    for(i = 0; i < arr->length; i++)
    {
        if(arr->array[i])
        {
            arr->free_fn(arr->array[i]);
        }
    }
    free(arr->array);
    free(arr);

    return;
}

static void* array_list_get_item(struct array_list* arr, int i)
{
    if(i >= arr->length)
    {
        return NULL;
    }

    return arr->array[i];
}

static int array_list_expand_internal(struct array_list* arr, int max)
{
    void* t;
    int new_size;

    if(max < arr->size)
    {
        return 0;
    }
    new_size = PF_JSON_MAX(arr->size << 1, max);
    if(!(t = realloc(arr->array, new_size*sizeof(void*))))
    {
        return -1;
    }
    arr->array = (void**)t;
    (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*));
    arr->size = new_size;

    return 0;
}

static int array_list_put_item(struct array_list* arr, int idx, void* data)
{
    if(array_list_expand_internal(arr, idx))
    {
        return -1;
    }
    if(arr->array[idx]) arr->free_fn(arr->array[idx]);
    arr->array[idx] = data;
    if(arr->length <= idx) arr->length = idx + 1;

    return 0;
}

static int array_list_add_item(struct array_list* arr, void* data)
{
    return array_list_put_item(arr, arr->length, data);
}

static int array_list_get_len(struct array_list* arr)
{
    return arr->length;
}

static int pf_json_parse_int64(const char* buf, int64_t* retval)
{
    char buf_cmp[100];
    int64_t num64;
    int buf_cmp_len = 0;
    int orig_has_neg = 0;
    int recheck_has_neg = 0;
    char* buf_cmp_start = NULL;
    const char* buf_skip_space = NULL;

    if(sscanf(buf, "%" SCNd64, &num64) != 1)
    {
        PF_JSON_DEBUG("Failed to parse, sscanf != 1\n");
        return 1;
    }
    buf_skip_space = buf;
    orig_has_neg = 0;
    // Skip leading spaces
    while (isspace((int)*buf_skip_space) &&* buf_skip_space)
    {
        buf_skip_space++;
    }
    if(*buf_skip_space == '-')
    {
        buf_skip_space++;
        orig_has_neg = 1;
    }
    // Skip leading zeros, but keep at least one digit
    while (buf_skip_space[0] == '0' && buf_skip_space[1] != '\0')
    {
        buf_skip_space++;
    }
    if(buf_skip_space[0] == '0' && buf_skip_space[1] == '\0')
    {
        orig_has_neg = 0; // "-0" is the same as just plain "0"
    }

    if(errno == ERANGE)errno = 0;
    if(errno != ERANGE)
    {
        buf_cmp_start = buf_cmp;
        recheck_has_neg = 0;

        snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64);
        if(*buf_cmp_start == '-')
        {
            recheck_has_neg = 1;
            buf_cmp_start++;
        }
        // No need to skip leading spaces or zeros here.

        buf_cmp_len = strlen(buf_cmp_start);
        /**
        *         If the sign is different, or
        *         some of the digits are different, or
        *         there is another digit present in the original string
        *         then we NOT successfully parsed the value.
        */
        if(orig_has_neg != recheck_has_neg 
           || strncmp(buf_skip_space, buf_cmp_start, strlen(buf_cmp_start)) != 0 
           || (strlen(buf_skip_space) != (unsigned)buf_cmp_len 
               && isdigit((int)buf_skip_space[buf_cmp_len])))
        {
            errno = ERANGE;
        }
    }
    if(errno == ERANGE)
    {
        if(orig_has_neg)
        {
            num64 = INT64_MIN;
        }
        else
        {
            num64 = INT64_MAX;
        }
    }
    *retval = num64;

    return 0;
}

